﻿using System;
using System.Globalization;
using System.Threading;
using TY.UC.Common;
using TY.UC.Common.Extension;
using TY.UT.Pay.WxPay.lib;

namespace TY.UT.Pay.WxPay
{
    /// <summary>
    /// 刷卡支付工具类
    /// </summary>
    public class MicroPayHelper
    {
        /// <summary>
        /// 刷卡支付完整业务流程逻辑
        /// </summary>
        /// <param name="body">商品描述</param>
        /// <param name="tradeNo">商户订单号</param>
        /// <param name="totalFee">总金额，单位：分</param>
        /// <param name="authCode">支付授权码</param>
        /// <returns></returns>
        public static PayResult Run(string body, string tradeNo, float totalFee, string authCode)
        {
            LogHelper.WriteLog("调用微信支付接口请求参数：[body]=" + body + "，[tradeNo]=" + tradeNo + "，[totalFee]=" + totalFee +
                               "，[authCode]=" + authCode);
            WxPayData data = new WxPayData();
            data.SetValue("auth_code", authCode);//授权码
            data.SetValue("body", body);//商品描述
            data.SetValue("total_fee", Convert.ToInt32(totalFee * 100));//总金额
            data.SetValue("out_trade_no", tradeNo);//商户订单号
            WxPayData result = WxPayApi.Micropay(data); //提交被扫支付，接收返回结果

            //如果提交被扫支付接口调用失败
            if (!result.IsSet("return_code") || result.GetValue("return_code").ToString() == "FAIL")
            {
                LogHelper.WriteLog("微信支付失败，入参：[data]=" + data.JsonSe() + "，[result]=" + result.ToJson(), new Exception());
                return new PayResult
                {
                    trade_status = "40004"
                };
            }

            //签名验证
            result.CheckSign();
            LogHelper.WriteLog("微信支付验证签名正常");

            //刷卡支付直接成功
            if (result.GetValue("return_code").ToString() == "SUCCESS" &&
                result.GetValue("result_code").ToString() == "SUCCESS")
            {
                LogHelper.WriteLog("微信支付成功：[result]=" + result.ToJson());
                string timeStr = result.GetValue("time_end").ToString();
                return new PayResult
                {
                    trade_status = "10000",
                    trade_no = result.GetValue("transaction_id").ToString(),
                    gmt_payment = string.IsNullOrEmpty(timeStr)
                        ? DateTime.Now
                        : DateTime.ParseExact(timeStr, "yyyyMMddHHmmss", System.Globalization.CultureInfo.InvariantCulture)
                };
            }

            /******************************************************************
             * 剩下的都是接口调用成功，业务失败的情况
             * ****************************************************************/
            //1）业务结果明确失败
            if (result.GetValue("err_code").ToString() != "USERPAYING" &&
            result.GetValue("err_code").ToString() != "SYSTEMERROR")
            {
                LogHelper.WriteLog("微信支付接口调用成功，业务失败：[result]=" + result.ToJson());
                return new PayResult
                {
                    trade_status = "40004"
                };
            }

            //2）不能确定是否失败，需查单
            //用商户订单号去查单
            string outTradeNo = data.GetValue("out_trade_no").ToString();

            //确认支付是否成功,每隔一段时间查询一次订单，共查询10次
            int queryTimes = 10;//查询次数计数器
            while (queryTimes-- > 0)
            {
                int succResult = 0;//查询结果
                WxPayData queryResult = Query(outTradeNo, out succResult);
                //如果需要继续查询，则等待2s后继续
                switch (succResult)
                {
                    case 2:
                        LogHelper.WriteLog("微信轮询第" + (10 - queryTimes) + "次未查询到结果：[queryResult]=" + queryResult.ToJson());
                        Thread.Sleep(2000);
                        break;
                    case 1:
                        LogHelper.WriteLog("微信轮询第" + (10 - queryTimes) + "次交易成功：[queryResult=]" + queryResult.ToJson());
                        string timeStr = queryResult.GetValue("time_end").ToString();
                        return new PayResult
                        {
                            trade_status = "10000",
                            trade_no = queryResult.GetValue("transaction_id").ToString(),
                            gmt_payment = string.IsNullOrEmpty(queryResult.GetValue("time_end").ToString())
                                ? DateTime.Now
                                : DateTime.ParseExact(timeStr, "yyyyMMddHHmmss", System.Globalization.CultureInfo.InvariantCulture)
                        };
                    default:
                        LogHelper.WriteLog("微信轮询第" + (10 - queryTimes) + "次交易失败：[queryResult]=" + queryResult.ToJson(), new Exception());
                        return new PayResult
                        {
                            trade_status = "40004"
                        };
                }
            }

            //确认失败，则撤销订单
            LogHelper.WriteLog("微信轮询未查询到结果，调用撤销交易接口");
            if (Cancel(outTradeNo))
            {
                return new PayResult
                {
                    trade_status = "10003"
                };
            }
            LogHelper.WriteLog("微信撤销交易失败，入参：[data]=" + data.ToJson(), new Exception());
            throw new WxPayException("微信撤销交易失败");
        }
        /// <summary>
        /// 查询订单情况
        /// </summary>
        /// <param name="outTradeNo">商户订单号</param>
        /// <param name="succCode">查询订单结果：0表示订单不成功，1表示订单成功，2表示继续查询</param>
        /// <returns></returns>
        public static WxPayData Query(string outTradeNo, out int succCode)
        {
            LogHelper.WriteLog("微信开始查询：[outTradeNo]=" + outTradeNo);
            WxPayData queryOrderInput = new WxPayData();
            queryOrderInput.SetValue("out_trade_no", outTradeNo);
            WxPayData result = WxPayApi.OrderQuery(queryOrderInput);
            LogHelper.WriteLog("微信查询到的结果：[result]=" + result.ToJson());

            if (result.GetValue("return_code").ToString() == "SUCCESS"
                && result.GetValue("result_code").ToString() == "SUCCESS")
            {
                //支付成功
                switch (result.GetValue("trade_state").ToString())
                {
                    case "SUCCESS":
                        succCode = 1;
                        return result;
                    case "USERPAYING":
                        succCode = 2;
                        return result;
                    case "REFUND"://转入退款 
                    case "CLOSED"://已关闭 
                    case "REVOKED"://已撤销（刷卡支付）
                    case "PAYERROR"://支付失败(其他原因，如银行返回失败)
                    case "NOTPAY"://未支付 
                        succCode = 0;
                        return result;
                }
            }

            //如果返回错误码为“此交易订单号不存在”则直接认定失败
            succCode = result.GetValue("err_code").ToString() == "ORDERNOTEXIST" ? 0 : 2;
            return result;
        }
        /// <summary>
        /// 子商户的查询订单情况
        /// </summary>
        /// <param name="outTradeNo">商户订单号</param>
        /// <param name="succCode">查询订单结果：0表示订单不成功，1表示订单成功，2表示继续查询</param>
        /// <returns></returns>
        public static WxPayData SubMerQuery(string outTradeNo,string sub_appid,string sub_mch_id, out int succCode, out decimal payamount)
        {
            payamount = 0;
            LogHelper.WriteLog("微信开始查询：[outTradeNo]=" + outTradeNo);
            WxPayData queryOrderInput = new WxPayData();
            queryOrderInput.SetValue("out_trade_no", outTradeNo);

            queryOrderInput.SetValue("sub_appid", sub_appid);
            queryOrderInput.SetValue("sub_mch_id", sub_mch_id);

            WxPayData result = WxPayApi.SubMerOrderQuery(queryOrderInput);
            LogHelper.WriteLog("微信查询到的结果：[result]=" + result.ToJson());

            if (result.GetValue("return_code").ToString() == "SUCCESS"
                && result.GetValue("result_code").ToString() == "SUCCESS")
            {
                //支付成功
                switch (result.GetValue("trade_state").ToString())
                {
                    case "SUCCESS":
                        decimal.TryParse(result.GetValue("total_fee").ToString(), out payamount);
                        payamount = payamount / 100;
                        succCode = 1;
                        return result;
                    case "USERPAYING":
                        succCode = 2;
                        return result;
                    case "REFUND"://转入退款 
                    case "CLOSED"://已关闭 
                    case "REVOKED"://已撤销（刷卡支付）
                    case "PAYERROR"://支付失败(其他原因，如银行返回失败)
                    case "NOTPAY"://未支付 
                        succCode = 0;
                        return result;
                }
            }

            //如果返回错误码为“此交易订单号不存在”则直接认定失败
            succCode = result.GetValue("err_code").ToString() == "ORDERNOTEXIST" ? 0 : 2;
            return result;
        }
        /// <summary>
        /// 查询订单情况
        /// </summary>
        /// <param name="outTradeNo">商户订单号</param>
        /// <param name="succCode">查询订单结果：0表示订单不成功，1表示订单成功，2表示继续查询</param>
        /// <returns></returns>
        public static WxPayData Query(string outTradeNo, out int succCode, out decimal payamount)
        {
            payamount = 0;
            LogHelper.WriteLog("微信开始查询：[outTradeNo]=" + outTradeNo);
            WxPayData queryOrderInput = new WxPayData();
            queryOrderInput.SetValue("out_trade_no", outTradeNo);
            WxPayData result = WxPayApi.OrderQuery(queryOrderInput);
            LogHelper.WriteLog("微信查询到的结果：[result]=" + result.ToJson());

            if (result.GetValue("return_code").ToString() == "SUCCESS"
                && result.GetValue("result_code").ToString() == "SUCCESS")
            {
                //支付成功
                switch (result.GetValue("trade_state").ToString())
                {
                    case "SUCCESS":
                        decimal.TryParse(result.GetValue("total_fee").ToString(), out payamount);
                        payamount = payamount / 100;
                        succCode = 1;
                        return result;
                    case "USERPAYING":
                        succCode = 2;
                        return result;
                    case "REFUND"://转入退款 
                    case "CLOSED"://已关闭 
                    case "REVOKED"://已撤销（刷卡支付）
                    case "PAYERROR"://支付失败(其他原因，如银行返回失败)
                    case "NOTPAY"://未支付 
                        succCode = 0;
                        return result;
                }
            }

            //如果返回错误码为“此交易订单号不存在”则直接认定失败
            succCode = result.GetValue("err_code").ToString() == "ORDERNOTEXIST" ? 0 : 2;
            return result;
        }
        /// <summary>
        /// 查询APP支付订单情况
        /// </summary>
        /// <param name="outTradeNo">商户订单号</param>
        /// <param name="succCode">查询订单结果：0表示订单不成功，1表示订单成功，2表示继续查询</param>
        /// <returns></returns>
        public static WxPayData APPQuery(string outTradeNo, out int succCode)
        {
            LogHelper.WriteLog("微信开始查询：[outTradeNo]=" + outTradeNo);
            WxPayData queryOrderInput = new WxPayData();
            queryOrderInput.SetValue("out_trade_no", outTradeNo);
            WxPayData result = WxPayApi.OrderQuery(queryOrderInput, 6, true);
            LogHelper.WriteLog("微信查询到的结果：[result]=" + result.ToJson());

            if (result.GetValue("return_code").ToString() == "SUCCESS"
                && result.GetValue("result_code").ToString() == "SUCCESS")
            {
                //支付成功
                switch (result.GetValue("trade_state").ToString())
                {
                    case "SUCCESS":
                        succCode = 1;
                        return result;
                    case "USERPAYING":
                        succCode = 2;
                        return result;
                    case "REFUND"://转入退款 
                    case "CLOSED"://已关闭 
                    case "REVOKED"://已撤销（刷卡支付）
                    case "PAYERROR"://支付失败(其他原因，如银行返回失败)
                    case "NOTPAY"://未支付 
                        succCode = 0;
                        return result;
                }
            }
            else
            {
                succCode = 2;//订单查询失败
                return result;
            }

            //如果返回错误码为“此交易订单号不存在”则直接认定失败
            succCode = result.GetValue("err_code").ToString() == "ORDERNOTEXIST" ? 0 : 2;
            return result;
        }
        /// <summary>
        /// 查询APP支付订单情况
        /// </summary>
        /// <param name="outTradeNo">商户订单号</param>
        /// <param name="succCode">查询订单结果：0表示订单不成功，1表示订单成功，2表示继续查询</param>
        /// <returns></returns>
        public static WxPayData APPQuery(string outTradeNo, out int succCode, out decimal payamount)
        {
            payamount = 0;
            LogHelper.WriteLog("微信开始查询：[outTradeNo]=" + outTradeNo);
            WxPayData queryOrderInput = new WxPayData();
            queryOrderInput.SetValue("out_trade_no", outTradeNo);
            WxPayData result = WxPayApi.OrderQuery(queryOrderInput, 6, true);
            LogHelper.WriteLog("微信查询到的结果：[result]=" + result.ToJson());

            if (result.GetValue("return_code").ToString() == "SUCCESS"
                && result.GetValue("result_code").ToString() == "SUCCESS")
            {
                //支付成功
                switch (result.GetValue("trade_state").ToString())
                {
                    case "SUCCESS":
                        decimal.TryParse(result.GetValue("total_fee").ToString(), out payamount);
                        payamount = payamount / 100;
                        succCode = 1;
                        return result;
                    case "USERPAYING":
                        succCode = 2;
                        return result;
                    case "REFUND"://转入退款 
                    case "CLOSED"://已关闭 
                    case "REVOKED"://已撤销（刷卡支付）
                    case "PAYERROR"://支付失败(其他原因，如银行返回失败)
                    case "NOTPAY"://未支付 
                        succCode = 0;
                        return result;
                }
            }
            else
            {
                succCode = 2;//订单查询失败
                return result;
            }

            //如果返回错误码为“此交易订单号不存在”则直接认定失败
            succCode = result.GetValue("err_code").ToString() == "ORDERNOTEXIST" ? 0 : 2;
            return result;
        }
        /// <summary>
        /// 撤销订单，如果失败会重复调用10次
        /// </summary>
        /// <param name="outTradeNo">商户订单号</param>
        /// <param name="depth">调用次数，这里用递归深度表示</param>
        /// <returns>false表示撤销失败，true表示撤销成功</returns>
        public static bool Cancel(string outTradeNo, int depth = 0)
        {
            if (depth > 10)
            {
                return false;
            }

            LogHelper.WriteLog("微信第" + depth + "次撤销订单，入参：[outTradeNo]=" + outTradeNo);
            WxPayData reverseInput = new WxPayData();
            reverseInput.SetValue("out_trade_no", outTradeNo);
            WxPayData result = WxPayApi.Reverse(reverseInput, 10);//延长取消订单微信的等待时间
            LogHelper.WriteLog("微信撤销结果：[result]=" + result.ToJson());

            //接口调用失败
            if (result.GetValue("return_code").ToString() != "SUCCESS")
            {
                return false;
            }

            //如果结果为success且不需要重新调用撤销，则表示撤销成功
            if (result.GetValue("result_code").ToString() == "SUCCESS" && result.GetValue("recall").ToString() == "N")
            {
                return true;
            }
            return result.GetValue("recall").ToString() == "Y" && Cancel(outTradeNo, ++depth);
        }
        /// <summary>
        /// 撤销APP支付订单，如果失败会重复调用10次
        /// </summary>
        /// <param name="outTradeNo">商户订单号</param>
        /// <param name="depth">调用次数，这里用递归深度表示</param>
        /// <returns>false表示撤销失败，true表示撤销成功</returns>
        public static bool APPCancel(string outTradeNo, int depth = 0)
        {
            if (depth > 10)
            {
                return false;
            }

            LogHelper.WriteLog("微信第" + depth + "次撤销订单，入参：[outTradeNo]=" + outTradeNo);
            WxPayData reverseInput = new WxPayData();
            reverseInput.SetValue("out_trade_no", outTradeNo);
            WxPayData result = WxPayApi.Reverse(reverseInput, 10, true);//延长取消订单微信的等待时间
            LogHelper.WriteLog("微信撤销结果：[result]=" + result.ToJson());

            //接口调用失败
            if (result.GetValue("return_code").ToString() != "SUCCESS")
            {
                return false;
            }

            //如果结果为success且不需要重新调用撤销，则表示撤销成功
            if (result.GetValue("result_code").ToString() == "SUCCESS" && result.GetValue("recall").ToString() == "N")
            {
                return true;
            }
            return result.GetValue("recall").ToString() == "Y" && Cancel(outTradeNo, ++depth);
        }
    }
}